# 自定义权限认证装饰器
# 如果校验通过，返回；校验不通过

from flask import request, current_app
from libs.response import generate_response
from functools import wraps
from hashlib import md5
from model.user import ApiPermission, ApiToken
from libs.error_code import APIAuthorizedException
import time
# #https://www.jianshu.com/p/50ade6f2e4fd
# #eyJhbGciOiJIUzUxMiIsImlhdCI6MTY2MTMwNzU1OCwiZXhwIjoxNjYxMzA3NjE4fQ.eyJ1aWQiOjE1fQ.8qVjKIWhyQb_mvjRWb5x5sTLC3dlnlK8lOUXHX0rBi7Ua5sg51PPv5r_XM6QXZZYxxV3Bl5ooq0_JXyY09AOpA
# #每个内容之间用.分割
# #header:   base64enc({"alg":"HS512","iat":1661307558,"exp":1661307618})
# #playload:  base64enc({"uid":15})
# #signature: sha512(base64enc(header)+.+base64enc(playload)+serectkey)
from itsdangerous import TimedJSONWebSignatureSerializer as TJS
from itsdangerous import BadSignature, SignatureExpired
from libs.error_code import TokenFailException


def login(func):
    @wraps(func)  # 保留装饰的函数元数据，即不让被装饰函数的endpoint发生改变
    def inner(*args, **kwargs):
        # 增加一个token验证
        token = request.args.get("token")
        if token and verify_token(token):
            uid = verify_token(token)
            print(f"token is {uid}")
            result = func(*args, **kwargs)
            return result
        elif not token and api_auth():
            result = func(*args, **kwargs)
            return result
        else:
            return generate_response(status_code=10002, message="auth fail")
            # raise APIAuthorizedException
    return inner


def api_auth():
    # 客户端需要传递的4个参数
    params = request.args
    appid = params.get("appid")
    salt = params.get("salt")  # 盐值
    sign = params.get("sign")  # 签名
    timestamp = params.get("timestamp")  # 时间戳

    # 请求时间超过10分钟就失效
    if time.time() - int(float(timestamp)) > 600:
        return False

    # 验证appid是否存在
    api_token = ApiToken.query.filter_by(appid=appid).first()
    if not api_token:
        return False

    # 验证有没有此url和方法的权限
    # http://127.0.0.1:9000/v1/monitor   GET
    # request.path：/v1/monitor， request.method.lower()：get
    if not has_permission(api_token, request.path, request.method.lower()):
        return False
    # 获取数据库里的密钥
    secretkey = api_token.secretkey
    # 生成服务端的签名
    # 可以加上时间戳来防止签名被别人盗取，重复访问
    user_sign = appid + salt + secretkey + timestamp
    m1 = md5()
    m1.update(user_sign.encode(encoding="utf-8"))
    # 判断客户端传递过来的签名和服务端生成签名是否一致
    if sign != m1.hexdigest():
        raise APIAuthorizedException
        # return False
    else:
        return True


# url验证
# 192.168.2.152:9000/v1/monitor  GET
def has_permission(api_token, url, method):
    # 客户端请求的方法和url
    # get/v1/monitor
    mypermission = method + url
    # 获取此api_token对象的所有url权限
    all_permission = [permission.method_type + permission.url
                      for permission in api_token.manage]
    if mypermission in all_permission:
        return True
    else:
        return False


# https
# 加时间戳
# JWT JWS JWE
# https://www.jianshu.com/p/50ade6f2e4fd
# 生成token
def create_token(uid):
    # 第一个参数传递内部私钥,测试环境写死
    # 第二个参数是有效期
    s = TJS(current_app.config["SECRET_KEY"], expires_in=current_app.config["EXPIRES_IN"])
    # s = TJS("123456", expires_in=10)
    token = s.dumps({"uid": uid}).decode("ascii")
    return token

# 验证token
def verify_token(token):
    # 用服务器的SECRET_KEY生成一个对象
    s = TJS(current_app.config["SECRET_KEY"])
    try:
        data = s.loads(token)
    # 抛出签名错误的异常
    except BadSignature:
        print("xxxxxxxxxxxxxxxx")
        raise TokenFailException
        # raise RuntimeError("签名错误")
    # 抛出token过期的异常
    except SignatureExpired:
        raise RuntimeError("token过期")
    return data

# 签名认证 -- api授权
# 1. 客户端向服务管理人员请求appid和secretkey
# 2. 服务端提供appid和secretkey，以及相应的算法
